#include "SwapFileManager.h"
#include "P2Defs.h"
#include "../threads/system.h"


SwapFileManager::SwapFileManager() {
    int swapSize = MAX_SWAP_ENTRIES * PageSize;

    swapBM = new BitMap(MAX_SWAP_ENTRIES);
    fileSystem->Create(SWAP_FILE_NAME, swapSize) ;
    
    swapFile = fileSystem->Open(SWAP_FILE_NAME);

    if (!swapFile) {
        printf("SwapFileManager: Couldn't open swap file. Halting...\n");
        interrupt -> Halt();
    } else {
        printf("SwapFileManager: Swap file created successfully. Total size is %d bytes for %d swap pages.\n", swapSize, MAX_SWAP_ENTRIES);
    }

    swapLock = new Lock("SwapFileLock");
}

int SwapFileManager::readPageFromSwapFile(int ppn, int offset) {

    int ret;

    //get a lock.
    swapLock -> Acquire();

    // DEBUG('D', "SwapFileManager: ReadFromSwapFile. Offset=%d, PPN=%d.\n", offset, ppn);

    // get the memory pointer, and location in swap file
    char* buffer = (char*) (machine->mainMemory + (ppn * PageSize));
    int pos = offset * PageSize;

    //check for valid address
    if (offset < 0) {
        DEBUG('W', "SwapFileManager: Offset %d is invalid.\n", offset);
        ret = -1;
    } else if (! swapBM ->Test(offset)) {
        // seee if there's any entry in my bitmap
        DEBUG('W', "SwapFileManager: Offset %d has no entry in swap file. Swap read at %d failed.\n", offset, offset);
        ret = -1;
    } else {
        // if everything is fine, do the read
        ret = swapFile -> ReadAt((char*) buffer, PageSize, pos);
        stats -> numDiskReads++;
        swapBM ->Clear(offset);
        // DEBUG('S', "SwapFileManager: Reading swap-page %d into memory PPN=%d. Mem Addr: 0x%x(%d)\n", offset, ppn, (int) buffer, (int) buffer);
        ret = 1;
    }

     // DEBUG('D', "SwapFileManager: ReadFromSwapFile end. Offset=%d, PPN=%d.\n", offset, ppn);

    swapLock -> Release();
    return ret;
}

int SwapFileManager::writePageToSwapFile(int ppn) {

    swapLock -> Acquire();
    // DEBUG('D', "SwapFileManager: WriteToSwapFile start. PPN=%d.\n", ppn);

    int swapLoc = swapBM ->Find();

    // no more swap space available
    if (swapLoc == -1) {
        printf("SwapFileManager: OUT OF SWAP SPACE. NOW HALTING.\n");
        swapLock -> Release();
        interrupt->Halt();
        
    } else {
        char* buffer = (char*) (machine->mainMemory + (ppn * PageSize));
        swapFile->WriteAt(buffer, PageSize, swapLoc * PageSize);
        stats -> numDiskWrites++;
        // DEBUG('S', "SwapFileManager: Writing memory PPN=%d into swap-page %d\n", ppn, swapLoc);
    }
    swapLock -> Release();
    // DEBUG('D', "SwapFileManager: WriteToSwapFile end. PPN=%d.\n", ppn);
    return swapLoc;
}

void SwapFileManager::markPageAsVoid(int swapOffset) {

    swapLock -> Acquire();
    // DEBUG('D', "SwapFileManager: MarkAsVoid start. swapOffset=%d.", swapOffset);
    
    if (swapOffset < 0 || swapOffset >= MAX_SWAP_ENTRIES) {
        DEBUG('W', "SwapFileManager: Invalid swap-entry(%d) passed into MARKPAGEASVOID(). Skipping.\n", swapOffset);
    } else {
        // DEBUG('S', "SwapFileManager: Marking swap-entry(%d) as void.\n", swapOffset);
        swapBM -> Clear(swapOffset);
    }

    // DEBUG('D', "SwapFileManager: MarkAsVoid end. swapOffset=%d.\n", swapOffset);
    swapLock -> Release();

}
